/**
 *  判断是否为空
 * @param {any} value
 * @param {Boolean} flag 是否包含0,'0'或false
 * @return {Boolen} true是空,false是非空
 */

const _empty = function(value, flag = true) {
	switch (typeof value) {
		case 'undefined':
			return true;
		case 'string':
			if (value.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g, '').length == 0) return true;
			if (flag && '0' === value) return true;
			break;
		case 'boolean':
			if (flag && !value) return true;
			break;
		case 'number':
			if (flag && (0 === value || isNaN(value))) return true;
			break;
		case 'object':
			if (null === value || value.length === 0) return true;
			// 排除{}
			for (let i in value) return false;
			return true;
	}
	return false;
}

/**
 *  检查数据类型
 * @param {any} o 待检查的数据
 * @param string type 数据类型
 * @return bool
 */
const _check = function(o, type) {
	// 转换为小写
	const temp = type?.toLowerCase() ?? '';
	switch (temp) {
		case 'string': // 验证是否字符串		
			return Object.prototype.toString.call(o).slice(8, -1) === 'String';
		case 'number': // 验证是否是否数字
			return Object.prototype.toString.call(o).slice(8, -1) === 'Number';
		case 'bool': // 验证是否是Boolean
			return Object.prototype.toString.call(o).slice(8, -1) === 'Boolean';
		case 'function': // 验证是否是函数
			return Object.prototype.toString.call(o).slice(8, -1) === 'Function';
		case 'null': //是否为null
			return Object.prototype.toString.call(o).slice(8, -1) === 'Null';
		case 'undefined': //是否undefined
			return Object.prototype.toString.call(o).slice(8, -1) === 'Undefined';
		case 'object': //是否对象
			return Object.prototype.toString.call(o).slice(8, -1) === 'Object';
		case 'array': //是否数组
			if (typeof Array.isArray === "function") return Array.isArray(o);
			else return Object.prototype.toString.call(o).slice(8, -1) === 'Array';
		case 'date': // 是否时间对象
			return Object.prototype.toString.call(o).slice(8, -1) === 'Date';
		case 'regexp': //是否正则
			return Object.prototype.toString.call(o).slice(8, -1) === 'RegExp';
		case 'error': // 是否错误对象
			return Object.prototype.toString.call(o).slice(8, -1) === 'Error';
		case 'symbol': //是否Symbol函数
			return Object.prototype.toString.call(o).slice(8, -1) === 'Symbol';
		case 'promise': // 是否Promise对象
			return Object.prototype.toString.call(o).slice(8, -1) === 'Promise';
		case 'set': // 是否Set对象
			return Object.prototype.toString.call(o).slice(8, -1) === 'Set';
		default:
			return false;
	}
}
class Socket {
	constructor(options = {}, heart = 0, repeat = 3, callback) {
		// socket状态
		this.socketStart = false;
		// 错误信息
		this.errorMsg = '';
		// 心跳参数
		this.heart = heart;
		// 心跳参数对象
		this.heartData = ''
		// 最少响应时间
		if (_check(options, 'object') && !_empty(options.heartData)) this.heartData = options.heartData
		//重连参数
		this.repeat = repeat;
		this.heartbeatTime = null;
		this._monitorSocketError();
		this._monitorSocketClose();
		this.options = {};
		if (_check(options, 'string')) this.options.url = options;
		if (_check(options, 'object') && !_empty(options.url)) this.options.url = options.url
		if (_empty(this.options)) this.errorMsg = 'websocket连接未配置url';
		//socket地址(必须以ws://或wss://开头)
		// #ifdef APP-PLUS
		if (this.options.url.includes('127.0.0.1'))
			this.errorMsg = 'H5和小程序支持，APP不支持127.0.0.1的socket测试,需要更改为本地IP';
		// #endif		
		if (!this.options?.url?.startsWith('ws://') && !this.options?.url?.startsWith('wss://'))
			this.errorMsg = 'websocket的url必须是ws://或wss://协议';
		//按照uniapp官方的websocket定义参数
		// #ifdef APP-PLUS || MP || H5
		if (!_empty(options.header)) this.options.header = options.header;
		// #endif
		// #ifdef MP-WEIXIN
		if (!_empty(options.method)) this.options.method = options.method;
		// #endif
		// #ifndef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU || MP-TOUTIAO
		if (!_empty(options.protocols)) this.options.protocols = options.protocols;
		// #endif
		this._init(callback);
	}

	/**
	 * 初始化
	 */
	_init(callback) {
		const that = this;
		//检测url
		if (that.options.url == '') {
			that.errorMsg = 'websocket的url不能为空';
			return false;
		}
		let options = that.options;
		options.complete = function() {};
		that.socketTask = uni.connectSocket(options);
		console.log(that.socketTask, 'that.socketTask')
		that.socketTask.onOpen(res => {
			that.socketStart = true;
			callback && callback();
			console.log('WebSocket连接已打开！');
			that.errorMsg = "";
			if (that.heart > 0) {
				that.getHeartbeat();
				// setTimeout(() => {
				// 	that.getHeartbeat();
				// }, that.heart * 1000);
			}
			return that;
		});
	}
	//监听Socket错误
	_monitorSocketError() {
		const that = this;
		if (that.socketTask) {
			that.socketTask.onerror = function(err) {
				that.socketStart = false;
				that.errorMsg = 'WebSocket连接打开失败，请检查！';
				console.log('error =>', that);
				if (that.repeat > 0) {
					setTimeout(function() {
						uni.hideLoading()
						uni.$emit('websocketRec')
						// that._init();
					}, that.repeat * 1000);
				}
			};
		}
	}

	//监听Socket关闭
	_monitorSocketClose() {
		const that = this;
		that.socketTask && that.socketTask.onClose(({
			code,
			reason
		}) => {
			console.log(code, reason)
			that.socketStart = false;
			that.errorMsg = 'WebSocket closed!';
			console.log('close =>', that);
		});
	}


	//心跳
	getHeartbeat(isAgain = false, data) {
		const that = this;
		if (isAgain) {
			clearTimeout(that.heartbeatTime)
			that.heartbeatTime = null
			that.heartData = data
			that.getHeartbeat()
		} else {
			that.send(that.heartData, (val) => {
				that.heartbeatTime = setTimeout(() => {
					// console.log(val, 'getHeartbeat')
					if (val) {
						that.getHeartbeat();
					} else {
						uni.$emit('websocketRec')
						// that._init()
					}
				}, that.heart * 1000);
			});
		}
		// console.log(that.heartData)

	}
	/**
	 * Socket给服务器发送消息
	 * 发送的数据结构规定如下:
	 * 1、type   string    类型,用户自定义，如login,logout
	 * 2、from   string    来源,可选pc,app,mini和web
	 * 3、data   object    发送的数据,可为空
	 */
	send(data, callback) {
		const that = this;
		let timer;
		if (that.socketStart) {
			that._send(data, callback);
		} else {
			timer = setInterval(() => {
				if (that.socketStart) {
					that._send(data, callback);
					clearInterval(timer);
				}
			}, 100);
		}
	}
	_send(data, callback) {
		const that = this;
		//组装发送信息
		let msg = data;
		//发送消息
		// console.log('发送内容：', msg);
		that.socketTask && that.socketTask.send({
			data: JSON.stringify(msg),
			success: () => {
				// console.log('成功')
				callback && callback(true);
			},
			fail: () => {
				// console.log('失败')
				callback && callback(false);
			}
		});
	}
	/**
	 * 同步信息发送(常见应用就是同步webrtc)
	 * 发送的数据结构规定如下:
	 * 1、type   string    类型,用户自定义，如login,logout
	 * 2、from   string    来源,可选pc,app,mini和web
	 * 3、data   object    发送的数据
	 * 其中data结构:
	 *     type    string    同步类型:mine同一帐号，group是群帐号
	 *     mid     string    同步者
	 *     gid     string    同步群
	 *     to      string    为空则同步所有设备(除自身外)，可取pc,app,mini和web,多个设备则用逗号分隔
	 *     data    object    同步的数据
	 */
	sync(data, callback, mid = 'woxiaoyao') {
		const that = this;
		//组装发送信息
		let msg = {};
		if (_empty(data.type) && _empty(data.mid) && _empty(data.from)) msg.data = data;
		else msg = data;
		//检验参数
		if (_empty(msg.type)) {
			console.log('错误提醒:发送数据没有类型');
			return false;
		}
		if (_empty(msg.from)) msg.from = that.from;
		if (_empty(msg.data.type)) msg.data.type = "mine";
		if (_empty(msg.data.mid)) msg.data.mid = mid;
		//发送消息
		that.send(msg, callback);
	}

	//Socket接收服务器发送过来的消息
	onmessage(callback) {
		const that = this;
		that.socketTask && that.socketTask.onMessage((res) => {
			let data = JSON.parse(res.data);
			// console.log('收到服务器内容：', data);
			callback && callback(data);
		});
	}
	//关闭Socket
	close(options = {}) {
		const that = this;
		console.log(that.socketTask, '关闭socket that.socketTask.close')
		if (_empty(options)) that.socketTask && that.socketTask.close();
		else that.socketTask && that.socketTask.close(options);
		that.socketStart = false;
	}
}



export default Socket;